// Have include guard.
#ifndef ASYNCRESULTFINISHSTATE_H
#define ASYNCRESULTFINISHSTATE_H

// Include files.
#include <boost/smart_ptr/shared_ptr.hpp>
#include "State.hpp"
#include "AsyncResult.hpp"
#include "AsyncResultStartState.hpp"

// Have prototype.
template<typename Result>
class AsyncResultStartState;

template<typename Result>
class AsyncResultEffectState;

/**
 *  The AsyncResultFinishState class waits on the AsyncResult of an AsyncResultStartState to finish.
 *  
 *  This class is untested.
 */
template<typename Result>
class AsyncResultFinishState :
	public State
{
	friend class AsyncResultEffectState<Result>;
	
	public:
		/**
		 *  Constructor for AsyncResultFinishState.
		 */
		AsyncResultFinishState(AsyncResultStartState<Result> &startState) :
			m_startState(startState)
		{
		}
		
		/**
		 *  Destructor for AsyncResultFinishState.
		 */
		virtual ~AsyncResultFinishState()
		{
		}
		
		/**
		 *  Adds a transition that specifies which state to transition if the async operation finishes with the specified result.
		 *  
		 *  @param result The result for the transition.
		 *  @param state The State to transition to.
		 */
		void AddTransition(Result result, State &state)
		{
			if (m_doneCodesByResult.count(result) > 0)
				AddTransition(m_doneCodesByResult[result], state);
		}
	protected:
		virtual void HandlePeriodic()
		{
			boost::shared_ptr<AsyncResult<Result> > asyncResult = m_startState.GetAsyncResult();
			
			if (asyncResult->GetIsFinished())
			{
				Result result = asyncResult->GetResult();
				
				if (m_doneCodesByResult.count(result) > 0)
					SetDoneCode((*(m_doneCodesByResult.find(result))).second);
				else
				{
					// TODO: Report missing done code for result.
					
					SetDoneCode(StateDoneCode::GetGenericErrorCode());
				}
			}
		}
		
		/**
		 *  Adds a mapping to map a result from the async operation to a StateDoneCode.
		 *  
		 *  @param result The result to map from.
		 *  @param doneCode The StateDoneCode to transition to.
		 */
		void AddResultDoneCodeMapping(Result result, StateDoneCode doneCode)
		{
			std::pair<Result, StateDoneCode> p(result, doneCode);
			
			m_doneCodesByResult.insert(p);
		}
	private:
		AsyncResultStartState<Result> &m_startState;
		std::map<Result, StateDoneCode> m_doneCodesByResult;
};

#endif // #ifndef ASYNCRESULTFINISHSTATE_H 
